//-----------------------------------------------------------------------------
// Safeproxyd.
//-----------------------------------------------------------------------------

#include "safeproxy.h"

#include <event.h>
#include <expbuf.h>

#include "daemon.h"

// includes
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>




/* Get a consistent bool type */
#if HAVE_STDBOOL_H
# include <stdbool.h>
#else
  typedef enum {false = 0, true = 1} bool;
#endif



static void node_event_handler(int hid, short flags, void *data);



typedef struct {
	int port;
	int maxconns;
	bool verbose;
	bool daemonize;
	char *username;
	char *pid_file;
	char *interface;
} settings_t;


typedef struct {
	unsigned int out_bytes;
	unsigned int in_bytes;
	unsigned int requests;
} stats_t;





typedef struct {
	struct {
		int handle;
		struct event event;
		expbuf_t buffer;
	} in, out;
	bool active;
	bool verbose;
	enum {
		unknown,
		reading,
		resolving,
		connecting,
		transferring,
		closing
	} status;
	stats_t *stats;
	expbuf_t *common;
} node_t;


typedef struct {
	struct {
		int handle;		// socket handle
		struct event event;
	} servers[MAX_SERVERS];
	int active;					// number of active nodes.
	int maxconns;				// max number of nodes.
	node_t **nodes;					// array of node objects.
	bool verbose;
	stats_t *stats;
	expbuf_t *common;
} server_t;



typedef struct {
	struct event       clockevent;
	server_t          *server;
	stats_t           *stats;
} timeout_t;


//-----------------------------------------------------------------------------
// Global variables.
struct event_base *main_event_base = NULL;



//-----------------------------------------------------------------------------
// Since we will be limiting the number of connections we have, we will want to 
// make sure that the required number of file handles are avaialable.  For a 
// 'server', the default number of file handles per process might not be 
// 'enough, and this function will attempt to increase them, if necessary.
void set_maxconns(int maxconns) 
{
	struct rlimit rlim;
	
	assert(maxconns > 5);

	if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
		fprintf(stderr, "failed to getrlimit number of files\n");
		exit(1);
	} else {
	
		// we need to allocate twice as many handles because we may be receiving data from a file for each node.
		if (rlim.rlim_cur < maxconns)
			rlim.rlim_cur = (2 * maxconns) + 3;
			
		if (rlim.rlim_max < rlim.rlim_cur)
			rlim.rlim_max = rlim.rlim_cur;
		if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
			fprintf(stderr, "failed to set rlimit for open files. Try running as root or requesting smaller maxconns value.\n");
			exit(1);
		}
	}
}

//-----------------------------------------------------------------------------
// Given an address structure, will create a socket handle and set it for 
// non-blocking mode.
int new_socket(struct addrinfo *ai) {
	int sfd = INVALID_HANDLE;
	int flags;
	
	assert(ai != NULL);
	
	// bind the socket, and then set the socket to non-blocking mode.
	if ((sfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) >= 0) {
		if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 || fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
			perror("setting O_NONBLOCK");
			close(sfd);
			sfd = INVALID_HANDLE;
		}
	}
	
	return sfd;
}



//-----------------------------------------------------------------------------
// print some info to the user, so that they can know what the parameters do.
void usage(void) {
	printf(PACKAGE " " VERSION "\n");
	printf("-p <num>      TCP port to listen on (default: %d)\n", DEFAULT_PORT);
	printf("-l <ip_addr>  interface to listen on, default is INDRR_ANY\n");
	printf("-c <num>      max simultaneous connections, default is 1024\n");
	printf("\n");
	printf("-d            run as a daemon\n");
	printf("-P <file>     save PID in <file>, only used with -d option\n");
	printf("-u <username> assume identity of <username> (only when run as root)\n");
	printf("\n");
	printf("-v            verbose (print errors/warnings while in event loop)\n");
	printf("-h            print this help and exit\n");
	return;
}







//-----------------------------------------------------------------------------
// Handle the signal.  Any signal we receive can only mean that we need to exit.
static void sig_handler(const int sig) {
    printf("SIGINT handled (%d).\n", sig);
    assert(main_event_base != NULL);
    event_base_loopbreak(main_event_base);
}


//-----------------------------------------------------------------------------
// ignore SIGPIPE signals; we can use errno == EPIPE if we need that information
void ignore_sigpipe(void) {
	struct sigaction sa;
	
	sa.sa_handler = SIG_IGN;
	sa.sa_flags = 0;
	if (sigemptyset(&sa.sa_mask) == -1 || sigaction(SIGPIPE, &sa, 0) == -1) {
		perror("failed to ignore SIGPIPE; sigaction");
		exit(EXIT_FAILURE);
	}
}



///----------------------------------------------------------------------------

// used to clear a previously valid node.
void node_clear(node_t *node)
{
	assert(node != NULL);
	
	if (node->active == true) {
		assert(node->in.event.ev_base != NULL);
		event_del(&node->in.event);
	}
	assert(node->out.event.ev_base == NULL);
	
	assert(node->in.handle == INVALID_HANDLE);
	memset(&node->in.event, 0, sizeof(node->in.event));
	expbuf_clear(&node->in.buffer);
	
	assert(node->out.handle == INVALID_HANDLE);
	memset(&node->out.event, 0, sizeof(node->out.event));
	expbuf_clear(&node->out.buffer);

	node->active = false;
	
}



// used to initialise an invalid node structure.  The values currently in the 
// structure are unknown.   We will assign a handle, because the only time we 
// ever need to initiate a newly created struct is when we have received a 
// socket, and the
void node_init(node_t *node)
{
	assert(node != NULL);
	
	node->in.handle = INVALID_HANDLE;
	node->out.handle = INVALID_HANDLE;
	expbuf_init(&node->in.buffer, DEFAULT_BUFFSIZE);
	expbuf_init(&node->out.buffer, DEFAULT_BUFFSIZE);
	
	node->stats = NULL;
	
	node_clear(node);
	node->active = false;

	node->common = NULL;	
}



void node_read_headers(node_t *node)
{
	int i;
	char *ptr;
	int start, finish;
	char line[4096];
	
	assert(node != NULL);

	assert(node->in.buffer.length > 0);
	assert(node->out.buffer.length == 0);

	ptr = node->in.buffer.data;

	// read the data that we already have.
	start = 0;
	for(i=0; i < node->in.buffer.length; i++) {

		if (node->verbose > 2) { printf("In: pos:%d, char:'%c' (%d)\n", i, *ptr, *ptr); }
	
		if (*ptr == '\n') {
			finish = i;
			strncpy(line, node->in.buffer.data+start, finish-start);
		}

		ptr++;
	}

	assert(0);
	
	// Try to find the 'hosts' entry.
	// if the Host entry is found, then do a DNS lookup for the host.
	// Look at our dnscache list for results.
	// if not in the dnscache, then do a dns lookup.
	// if doing dns lookup, then we need to change the status to 'resolving' and then wait for teh results which should go to a callback function.
}


void node_transfer(node_t *node)
{
	assert(node != NULL);
	assert(0);	// code not complete.
}




// A connection was closed.   We need to figure out what connection it
// actually was that closed, and handle it appropriately.
void node_close(node_t *node, int hid)
{
	assert(node != NULL);
	assert(hid >= 0);
	
	// this code is not complete.
	assert(0);

	assert(hid == node->in.handle || hid == node->out.handle);
	
	if (hid == node->in.handle) {
		// this was our originating socket, so we can shut everything down
		// regardless of state because we cant supply data to the socket that
		// requested it.
		if (node->verbose > 0) printf("Node[%d] closed while reading.\n", node->in.handle);
	
		node->in.handle = INVALID_HANDLE;
		if (node->out.handle != INVALID_HANDLE) {
			close(node->out.handle);
			node->out.handle = INVALID_HANDLE;
		}
	
		// need to kill the events for these handles.
		event_del(&node->in.event);
		if (node->out.handle != INVALID_HANDLE) {
			event_del(&node->out.event);
		}
	
		node_clear(node);
		assert(node->active == false);
	
	}
	else {
		// this was our remote socket.  We can return the information that we have
		// got, and then close the socket.
		if (node->verbose > 0) printf("Node[%d] remote closed while reading.\n", node->in.handle);
		assert(hid == node->out.handle);
		assert(node->status == transferring);
		assert(node->status != closing);

		// need to kill events for this handle.
		event_del(&node->out.event);
	
		node->status = closing;
		node->out.handle = INVALID_HANDLE;

	}
}


void node_data_read(node_t *node, int hid)
{
	int res;

	assert(node != NULL);
	assert(hid > 0);

	// the 'common' buffer should have space available, and it should be
	// empty.  We only use this buffer to read in data, and since we add the
	// data to the queue immediately, then we dont need to keep it in the
	// buffer.
	assert(node->common->length == 0);
	assert(node->common->max > 0);
	assert(node->common->data != NULL);

	// read the data from the socket into our 'common' buffer.
	res = read(hid, node->common->data, node->common->max);
	if (res > 0) {

		assert(res <= node->common->max);
		node->common->length = res;

		// if we pulled out the max we had avail in our buffer, that means we
		// can pull out more at a time.
		if (res == node->common->max) {
			expbuf_shrink(node->common, node->common->max * 2);
		}

		if (hid == node->in.handle) {
			expbuf_add(&node->in.buffer, node->common->data, node->common->length);
			node->stats->in_bytes += res;
		}
		else {
			assert(hid == node->out.handle);
			expbuf_add(&node->out.buffer, node->common->data, node->common->length);
			node->stats->out_bytes += res;
		}

		expbuf_clear(node->common);

		assert(node->in.buffer.length <= node->in.buffer.max);
		assert(node->out.buffer.length <= node->out.buffer.max);

		assert(node->active);
		if (node->in.buffer.length > 0) {

			assert(node->status != unknown);

			switch (node->status) {
				case reading:
						node_read_headers(node);
					break;

				case transferring:
				case closing:
						node_transfer(node);
					break;

				case resolving:
				case connecting:
					break;

				default:
					break;
			}
		}
	}
	else if (res == 0) {
		node_close(node, hid);
	}
	else {
		assert(res == -1);
		if (errno != EAGAIN && errno != EWOULDBLOCK) {
			node_close(node, hid);
		}
	}
}


void node_data_write(node_t *node, int hid) {

	int res;
	
	assert(node != NULL);
	assert(hid > 0);

	// we've requested the event, so we should have data to process.
	assert(node->out.buffer.length > 0);
	assert(node->out.buffer.length <= node->out.buffer.max);

	res = send(hid, node->out.buffer.data, node->out.buffer.length, 0);
	if (res > 0) {
		// we managed to send some, or maybe all....
		assert(res <= node->out.buffer.length);
		expbuf_purge(&node->out.buffer, res);
	}
	else if (res == 0) {
		node_close(node, hid);
		assert(node->active == false);
	}
	else {
		assert(res == -1);
		if (errno != EAGAIN && errno != EWOULDBLOCK) {
			node_close(node, hid);
		}
	}

	// if we have sent everything, then we dont need to wait for a WRITE event anymore, so we need to re-establish the events.
	if (node->active) {
		if (hid == node->in.handle && node->out.buffer.length == 0) {
			if (event_del(&node->out.event) != -1) {
				event_set(&node->out.event, hid, EV_READ | EV_PERSIST, node_event_handler, (void *)node);
				event_base_set(node->out.event.ev_base, &node->out.event);
				event_add(&node->out.event, 0);
			}
		}
		else if (hid == node->out.handle && node->in.buffer.length == 0) {
			if (event_del(&node->in.event) != -1) {
				event_set(&node->in.event, hid, EV_READ | EV_PERSIST, node_event_handler, (void *)node);
				event_base_set(node->in.event.ev_base, &node->in.event);
				event_add(&node->in.event, 0);
			}
		}
	}
}


// this function is called when we have received a new socket.   We need to 
// create a new node, and add it to our node list.  We need to pass to the node 
// any pointers to other sub-systems that it will need to have, and then we 
// insert the node into the 'node-circle' somewhere.  Finally, we need to add 
// the new node to the event base.
static void node_event_handler(int hid, short flags, void *data)
{
	node_t *node;
	
	assert(hid >= 0);
	assert(flags != 0);
	assert(data != NULL);
	
	node = (node_t *) data;

	assert(node != NULL);
	assert(node->in.handle == hid || node->out.handle == hid);
	assert(node->stats != NULL);
	assert(node->active == true);
	
	if (flags & EV_READ) {
		node_data_read(node, hid);
	}

	assert(node->common != NULL);
	assert(node->common->length == 0);
		
	if (flags & EV_WRITE && node->active) {		
		node_data_write(node, hid);
	}
}



void node_free(node_t *node)
{
	assert(node != NULL);

	assert(node->in.handle == INVALID_HANDLE);
	assert(node->out.handle == INVALID_HANDLE);

	expbuf_free(&node->in.buffer);
	expbuf_free(&node->out.buffer);
}



void save_pid(const pid_t pid, const char *pid_file) {
	FILE *fp;
	
	assert(pid_file != NULL);
	assert(pid > 1);
	
	if ((fp = fopen(pid_file, "w")) == NULL) {
		fprintf(stderr, "Could not open the pid file %s for writing\n", pid_file);
		return;
	}
	
	fprintf(fp,"%ld\n", (long)pid);
	if (fclose(fp) == -1) {
		fprintf(stderr, "Could not close the pid file %s.\n", pid_file);
		return;
	}
}

void remove_pidfile(const char *pid_file) {
  assert(pid_file != NULL);
  
  if (unlink(pid_file) != 0) {
		fprintf(stderr, "Could not remove the pid file %s.\n", pid_file);
  }
}



// lose root privileges if we have them
int drop_privs(char *username)
{
	struct passwd *pw;
	
	assert(username != NULL);
	assert(username[0] != '\0');
  
	if (getuid() == 0 || geteuid() == 0) {
		if (username == 0 || *username == '\0') {
			fprintf(stderr, "can't run as root without the -u switch\n");
			return 1;
		}
		if ((pw = getpwnam(username)) == 0) {
			fprintf(stderr, "can't find the user %s to switch to\n", username);
			return 1;
		}
		if (setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) {
			fprintf(stderr, "failed to assume identity of user %s\n", username);
			return 1;
		}
	}
	return(0);
}





//-----------------------------------------------------------------------------
// Initialise and return a server struct that we will use to control the nodes 
// that we are connected to.   We will bind the listening port on the socket.
server_t *server_new(int port, int maxconns, char *address)
{
	server_t *server = NULL;
  struct linger ling = {0, 0};
	struct addrinfo *ai;
	struct addrinfo *next;
	struct addrinfo hints;
	char port_buf[NI_MAXSERV];
	int error;
	int index;
	int flags =1;
	
	assert(port > 0);
	assert(maxconns > 5);
	assert(address == NULL || (address != NULL && address[0] != '\0'));
	
	memset(&hints, 0, sizeof (hints));
	hints.ai_flags = AI_PASSIVE|AI_ADDRCONFIG;
	hints.ai_family = AF_UNSPEC;
	hints.ai_protocol = IPPROTO_TCP;
	hints.ai_socktype = SOCK_STREAM;

	snprintf(port_buf, NI_MAXSERV, "%d", port);
  error = getaddrinfo(address, port_buf, &hints, &ai);
	if (error != 0) {
		if (error != EAI_SYSTEM)
			fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
		else
			perror("getaddrinfo()");
		return(NULL);
	}

	server = (server_t *) malloc(sizeof(server_t));
	assert(server != NULL);

	for(index=0; index<MAX_SERVERS; index++) {
		server->servers[index].handle = INVALID_HANDLE;
	}
	
	// We will create an array of empty pointers for our nodes.  A pointer
	// doesn't take up much space, and we already know what our maximum is.
	// It will save a lot of detail in having to resize this array dynamically.
	server->maxconns = maxconns;
	assert(maxconns > 0);
	server->nodes = (node_t **) malloc(sizeof(node_t *) * maxconns);
	for (index=0; index<maxconns; index++) {
		server->nodes[index] = NULL;
	}

	assert(MAX_SERVERS > 0);
	index	= 0;
	for (next=ai; next && index < MAX_SERVERS; next=next->ai_next) {
	
		assert(server);
		assert(server->servers[index].handle == INVALID_HANDLE);
	
		// create the new socket.  if that fails, free the memory we've already allocated, and return NULL.
		server->servers[index].handle = new_socket(next);
		if (server->servers[index].handle == INVALID_HANDLE) {
			freeaddrinfo(ai);
			free(server);
			return(NULL);
		}

		setsockopt(server->servers[index].handle, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
		setsockopt(server->servers[index].handle, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
		setsockopt(server->servers[index].handle, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
// 		setsockopt(server->servers[index].handle, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));

    if (bind(server->servers[index].handle, next->ai_addr, next->ai_addrlen) == -1) {
			if (errno != EADDRINUSE) {
				perror("bind()");
				close(server->servers[index].handle);
				freeaddrinfo(ai);
				free(server);
				return(NULL);
			}
            
			close(server->servers[index].handle);
			server->servers[index].handle = INVALID_HANDLE;
			continue;
		} else {
			if (listen(server->servers[index].handle, 1024) == -1) {
				perror("listen()");
				close(server->servers[index].handle);
				freeaddrinfo(ai);
				free(server);
				return(NULL);
			}
			else {
				index++;
			}
    }
	}

	assert(server->servers[0].handle != INVALID_HANDLE);
    
	freeaddrinfo(ai);

	assert(server != NULL);
	server->stats = NULL;
	server->verbose = false;

	server->common = (expbuf_t *) malloc(sizeof(expbuf_t));
	expbuf_init(server->common, DEFAULT_BUFFSIZE);

	return(server);
}


// this function is called when we have received a new socket.   We need to 
// create a new node, and add it to our node list.  We need to pass to the node 
// any pointers to other sub-systems that it will need to have, and then we 
// insert the node into the 'node-circle' somewhere.  Finally, we need to add 
// the new node to the event base.
static void server_event_handler(int hid, short flags, void *data)
{
	server_t *server;
	socklen_t addrlen;
	struct sockaddr_storage addr;
	int sfd;
	node_t *node = NULL;
	
	assert(hid >= 0);
	assert(data != NULL);
	
  server = (server_t *) data;

	addrlen = sizeof(addr);
	if ((sfd = accept(hid, (struct sockaddr *)&addr, &addrlen)) == -1) {
		if (errno == EAGAIN || errno == EWOULDBLOCK) {
				/* these are transient, so don't log anything */
		} else if (errno == EMFILE) {
			if (server->verbose > 0)
					fprintf(stderr, "Too many open connections\n");
// 			accept_new_conns(false);
		} else {
			perror("accept()");
		}
	}
	
	// mark socket as non-blocking
	if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 || fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
			perror("setting O_NONBLOCK");
			close(sfd);
	}

	if (server->verbose) printf("New Connection [%d]\n", sfd);


	assert(node == NULL);
	if (sfd < server->maxconns && server->nodes[sfd] == NULL) {
		if (server->verbose) printf("Creating a new node\n");

		node = (node_t *) malloc(sizeof(node_t));
		node_init(node);

		server->nodes[sfd] = node;
		node->in.handle = sfd;
		node->verbose = server->verbose;
		
		assert(node->active == false);
		node->active = true;
		node->status = reading;

		assert(server->stats != NULL);
		node->stats = server->stats;

		assert(server->common != NULL);
		assert(node->common == NULL);
		node->common = server->common;
	}
	else {
	
		node = server->nodes[sfd];
		if (node->active == false) {
			if (server->verbose) printf("Re-using an existing node\n");

			node->in.handle = sfd;
			node->active = true;

			assert(node->status == unknown);
			node->status = reading;

			assert(server->common != NULL);
			assert(node->common == server->common);

			assert(node->in.buffer.length == 0);
			assert(node->out.buffer.length == 0);
			assert(node->common->length == 0);
		}
		else {
			assert(node->in.handle != sfd);
			assert(0);

			// need to code for an instance where we use some other slot.
			
		}
	}
	
	assert(server->servers[0].event.ev_base != NULL);
	
	// setup the event handling...
	if (server->verbose) printf("Setting up event for %d\n", sfd);
	event_set(&node->in.event, sfd, EV_READ | EV_PERSIST, node_event_handler, (void *)node);
	event_base_set(server->servers[0].event.ev_base, &node->in.event);
	event_add(&node->in.event, 0);
}



//-----------------------------------------------------------------------------
// This function is called as a callback from the event system whenever a new 
// socket connection has been made against the listener socket.
void server_add_event(server_t *server, struct event_base *evbase)
{
	int index;
	
	assert(server != NULL);
	assert(evbase != NULL);

	// assume that we have at least one handle setup.
	assert(server->servers[0].handle != INVALID_HANDLE);

	for(index=0; index < MAX_SERVERS && server->servers[index].handle != INVALID_HANDLE; index++) {
		event_set(&server->servers[index].event, server->servers[index].handle, (EV_READ | EV_PERSIST), server_event_handler, (void *)server);
		event_base_set(evbase, &server->servers[index].event);
		if (event_add(&server->servers[index].event, NULL) == -1) {
			perror("event_add");
		}
	}
}


void server_cleanup(server_t *server)
{
	int index;
	
	assert(server != NULL);

	for (index=0; index < MAX_SERVERS && server->servers[index].handle != INVALID_HANDLE; index++) {
		close(server->servers[index].handle);
		server->servers[index].handle = INVALID_HANDLE;
		event_del(&server->servers[index].event);
	}

	assert(server->maxconns > 0);
	for (index=0; index < server->maxconns; index++) {
		if (server->nodes[index] != NULL) {
			node_free(server->nodes[index]);
			free(server->nodes[index]);
			server->nodes[index] = NULL;
		}
	}

	assert(server->common != NULL);
	expbuf_free(server->common);
	free(server->common);
	server->common = NULL;
}


void settings_init(settings_t *ptr)
{
	assert(ptr != NULL);

	ptr->port = DEFAULT_PORT;
	ptr->maxconns = DEFAULT_MAXCONNS;
	ptr->verbose = false;
	ptr->daemonize = false;
	ptr->username = NULL;
	ptr->pid_file = NULL;
	ptr->interface = NULL;
}

void settings_cleanup(settings_t *ptr) 
{
	assert(ptr != NULL);
	free(ptr);
}





static void timeout_handler(const int fd, const short which, void *arg) {
	struct timeval t = {.tv_sec = 1, .tv_usec = 0};
	timeout_t *ptr;
	int active;
	int index;
	unsigned int memlen, memmax;
	
	ptr  = (timeout_t *) arg;

	assert(ptr != NULL);
	assert(ptr->clockevent.ev_base != NULL);

	// reset the timer to go off again in 1 second.
	evtimer_del(&ptr->clockevent);
	evtimer_set(&ptr->clockevent, timeout_handler, arg);
	event_base_set(ptr->clockevent.ev_base, &ptr->clockevent);
	evtimer_add(&ptr->clockevent, &t);
	
 	assert(fd == INVALID_HANDLE);
	assert(ptr->server != NULL);

	assert(ptr->stats != NULL);

	assert(ptr->server->common->max > 0);
	assert(ptr->server->common->length == 0);

	active = 0;
	memlen = 0; memmax = 0;
	for(index=0; index < ptr->server->maxconns; index++) {
		if (ptr->server->nodes[index] != NULL) {
			if (ptr->server->nodes[index]->active) {
				active++;
			}
			memlen += ptr->server->nodes[index]->in.buffer.length;
			memmax += ptr->server->nodes[index]->in.buffer.max;
			memlen += ptr->server->nodes[index]->out.buffer.length;
			memmax += ptr->server->nodes[index]->out.buffer.max;
		}
	}
	memmax += ptr->server->common->max;
	
	if (ptr->stats->in_bytes || ptr->stats->out_bytes || ptr->stats->requests || active > 0) {
		printf("Bytes [%u/%u], Requests [%u], Active [%d], Mem [%u/%u]\n",
			ptr->stats->in_bytes, ptr->stats->out_bytes,
			ptr->stats->requests, active,
			memlen, memmax);
		ptr->stats->in_bytes = 0;
		ptr->stats->out_bytes = 0;
		ptr->stats->requests = 0;
	}
}



void timeout_init(timeout_t *ptr, struct event_base *base) 
{
	struct timeval t = {.tv_sec = 1, .tv_usec = 0};
	assert(ptr != NULL);
	assert(ptr->clockevent.ev_base == NULL);
	
	evtimer_set(&ptr->clockevent, timeout_handler, (void *) ptr);
	event_base_set(ptr->clockevent.ev_base, &ptr->clockevent);
	evtimer_add(&ptr->clockevent, &t);
	assert(ptr->clockevent.ev_base != NULL);
}




//-----------------------------------------------------------------------------
// Main... process command line parameters, and then setup our listening 
// sockets and event loop.
int main(int argc, char **argv) 
{
	int c;
	settings_t     *settings = NULL;
	server_t       *server   = NULL;
	timeout_t      *timeout  = NULL;
	stats_t        *stats    = NULL;

	// handle SIGINT 
	signal(SIGINT, sig_handler);
	
	// init settings
	settings = (settings_t *) malloc(sizeof(settings_t));
	assert(settings != NULL);
	settings_init(settings);

	// set stderr non-buffering (for running under, say, daemontools)
	setbuf(stderr, NULL);


	// process arguments 
	/// Need to check the options in here, there're possibly ones that we dont need.
	while ((c = getopt(argc, argv, "p:k:c:hvd:u:P:l:s:")) != -1) {
		switch (c) {
			case 'p':
				settings->port = atoi(optarg);
				assert(settings->port > 0);
				break;
			case 'c':
				settings->maxconns = atoi(optarg);
				assert(settings->maxconns > 0);
				break;
			case 'h':
				usage();
				exit(EXIT_SUCCESS);
			case 'v':
				settings->verbose++;
				break;
			case 'd':
				assert(settings->daemonize == false);
				settings->daemonize = true;
				break;
			case 'u':
				assert(settings->username == NULL);
				settings->username = optarg;
				assert(settings->username != NULL);
				assert(settings->username[0] != '\0');
				break;
			case 'P':
				assert(settings->pid_file == NULL);
				settings->pid_file = optarg;
				assert(settings->pid_file != NULL);
				assert(settings->pid_file[0] != '\0');
				break;
			case 'l':
				assert(settings->interface == NULL);
				settings->interface = strdup(optarg);
				assert(settings->interface != NULL);
				assert(settings->interface[0] != '\0');
				break;
				
			default:
				fprintf(stderr, "Illegal argument \"%c\"\n", c);
				return 1;
		}
	}

	if (settings->verbose) printf("Finished processing command-line args\n");

	// If needed, increase rlimits to allow as many connections as needed.
	if (settings->verbose) printf("Settings Max connections: %d\n", settings->maxconns);
	assert(settings->maxconns > 0);
 	set_maxconns(settings->maxconns);

	// if we are supplied with a username, drop privs to it.  This will only 
	// work if we are running as root, and is really only needed when running as 
	// a daemon.
	if (settings->username != NULL) {
		if (settings->verbose) printf("Dropping privs and changing username: '%s'\n", settings->username);
		if (drop_privs(settings->username) != 0) {
			usage();
			exit(EXIT_FAILURE);
		}
	}

	// daemonize if requested
	// if we want to ensure our ability to dump core, don't chdir to /
	if (settings->daemonize) {
		int res;
		if (settings->verbose) printf("Daemonising\n");
		res = daemon(0, settings->verbose);
		if (res == -1) {
			fprintf(stderr, "failed to daemon() in order to daemonize\n");
			exit(EXIT_FAILURE);
		}
	}

	// initialize main thread libevent instance
	if (settings->verbose) printf("Initialising the event system.\n");
	main_event_base = event_init();


	if (settings->verbose) printf("Ignoring SIGPIPE interrupts\n");
	ignore_sigpipe();
    
	// save the PID in if we're a daemon, do this after thread_init due to a 
	// file descriptor handling bug somewhere in libevent
	if (settings->daemonize && settings->pid_file) {
		if (settings->verbose) printf("Saving Pid file: %s\n", settings->pid_file);
		save_pid(getpid(), settings->pid_file);
	}

	// create and init the 'server' structure.
	if (settings->verbose) printf("Starting server listener on port %d.\n", settings->port);
	server = server_new(settings->port, settings->maxconns, settings->interface);
	if (server == NULL) {
		fprintf(stderr, "Failed to listen on port %d\n", settings->port);
		exit(EXIT_FAILURE);
	}
	assert(server != NULL);
	server->verbose = settings->verbose;

	
	
	// add the server to the event base
	assert(main_event_base != NULL);
	server_add_event(server, main_event_base);



	// initialise clock event.  The clock event is used to keep up our node 
	// network.  If we dont have enough connections, we will need to make some 
	// requests.  
	// create the timeout structure, and the timeout event.   This is used to 
	// perform certain things spread over time.   Such as indexing the 
	// 'complete' paths that we have, and ensuring that the 'chunks' parts are 
	// valid.
	if (settings->verbose) printf("Setting up Timeout event.\n");
	timeout = (timeout_t *) malloc(sizeof(timeout_t));

	assert(timeout != NULL);
	assert(main_event_base != NULL);
	if (settings->verbose) printf("Initialising timeout.\n");
	timeout_init(timeout, main_event_base);
	timeout->server = server;

	stats = (stats_t *) malloc(sizeof(stats_t));
	stats->out_bytes = 0;
	stats->in_bytes = 0;
	stats->requests = 0;

	server->stats = stats;
	timeout->stats = stats;


	

	// enter the event loop.
	if (settings->verbose) printf("Starting Event Loop\n\n");
	event_base_loop(main_event_base, 0);
    

	// cleanup 'server', which should cleanup all the 'nodes'
	server_cleanup(server);
	free(server);
    
	if (settings->verbose) printf("\n\nExiting.\n");
    
	// remove the PID file if we're a daemon
	if (settings->daemonize && settings->pid_file != NULL) {
		if (settings->verbose) printf("Removing pid file: %s\n", settings->pid_file);
		remove_pidfile(settings->pid_file);
	}

	assert(settings != NULL);
	settings_cleanup(settings);
	settings = NULL;

	return 0;
}


